﻿
package extremefx.browser {
	import extremefx.extensions.isNullOrEmpty;

	import flash.net.URLRequest;
	import flash.net.URLVariables;

	/**
	 * @author Marcelo Volmaro
	 */
	public final class Uri {
		private static const PATTERN:RegExp = /^((\w+):\/\/)?((\w+):?(\w+)?@)?([^\/\?:]+):?(\d+)?(\/?[^\?#]+)?\??([^#]+)?#?(\w*)/i;
        private var _url:String;
        private var _qsData:Object;
        
		public var scheme:String;
		public var userName:String;
		public var password:String;
		public var authority:String;
		
		/**
		 * Gets the port number of this URL.
		 * @return the port number, or -1 if the port is not set
		 */
		public var port:int;
		public var pathName:String;
		public var queryString:String;
		public var fragment:String;

		/**
         * Creates a URL by parsing the given spec within a specified context. 
         * The new URL is created from the given context URL and the spec argument as described in RFC2396 "Uniform Resource Identifiers : Generic * Syntax":
         * <scheme>://<authority><path>?<query>#<fragment>
         *
         * @param s The url
         */
		function Uri(url:String):void {
			if (isNullOrEmpty(url)) throw new Error("url can not be empty");
			
			var fix:Boolean = url.indexOf("/") == 0;
			if (fix){
				url = "a"+url;//the regex breaks if the uri does not starts with a letter -and/or- a slash
			}
			
            var result:Array = url.match(PATTERN);
            _url = url;								// http://user:pass@example.com:80/foo/bar.php?var1=foo&amp;var2=bar#abc
            scheme = result[2] || "";				// http
            userName = result[4] || "";				// user
            password = result[5] || "";				// pass
            authority = fix ? "": (result[6] || "");// example.com
            port = parseInt(result[7]) || -1;		// 80
            pathName = result[8] || "";				// /foo/bar.php
            queryString = result[9] || "";			// var1=foo&amp;var2=bar
            fragment = result[10] || "";			// abc
            
			var t:Object = new Object(),
			b:Array = queryString.split("&");
			
			for each (var pair:String in b){
				var parts:Array = pair.split("="),
				varname:String = parts[0],
				p:Array, 
				value:String = parts.length == 2 ? decodeURIComponent(parts[1]) : varname;
				
				if (varname.substr(0, 4) == "amp;") varname = varname.substr(4);
				if (isNullOrEmpty(varname)) continue;
				
				if ((p = varname.split(/(\[|\]\[|\])/))) {
                    for (var z:uint=0; z < p.length; z++) {
                        if (p[z] == ']' || p[z] == '[' || p[z] == '][') {
                            p.splice(z, 1);
                        }
                    }

                    for (var o:uint = 0; o < p.length-1; o++) {
                        if (!t[p[o]]) t[p[o]] = {};
                        t = t[p[o]];
                    }
                    
                    t[p[p.length-1]] = value;
                    
                } else {
                    t[varname] = value;
                }
			}

			_qsData = t;
		}
		
		public function get originalString():String {
			return _url;
		}
		
		public function get segments():Array {
			return pathName.split("/");
		}
		
		public function getUrlRequest():URLRequest {
			var req:URLRequest = new URLRequest(toString());
			return req; 
		}
		
		public function toString():String {
			var ret:String = "";
			if (!isNullOrEmpty(scheme)) ret += scheme + "://";
			
			if (!isNullOrEmpty(userName)) ret += userName;
			if (!isNullOrEmpty(password)) ret += ":"+password;
			
			if (!isNullOrEmpty(userName) || !isNullOrEmpty(password)) ret += "@";
			
			ret += authority;
			if (port > 0) ret += ":"+port;
			
			ret += pathName;
			
			var parts:Array = [];
			for (var key:String in _qsData){
				if (!_qsData.hasOwnProperty(key) || isNullOrEmpty(key)) continue;
				parts.push(key + "=" + encodeURIComponent(_qsData[key]));
			}
			
//			if (parts.length) ret += "?"+parts.join("&amp;");			if (parts.length) ret += "?"+parts.join("&");
			
			if (!isNullOrEmpty(fragment)) ret += "#"+fragment;
			return ret;
		}
		
		public static function convertToVariables(pData:Object):URLVariables{
			var post:URLVariables = new URLVariables();
			for (var key:String in pData) {
				if (pData.hasOwnProperty(key)) {
					post[key] = pData[key];
				}
			}
			
			return post;
		}
		
		public function getQueryAsPostData():URLVariables {
			return convertToVariables(_qsData);
		}
		
		public function get queryData():Object {
			return _qsData;
		}

		public function appendQueryData(pData:Object):void {
			for (var i:String in pData){
				if (!pData.hasOwnProperty(i)) continue;
				_qsData[i] = pData[i];
			}
		}
		
//		public function set queryStringData(pData:Object):void {
//			if (pData == null) {
//				_qsData = {};
//				
//			} else {
//				for (var i:String in pData){
//					if (!pData.hasOwnProperty(i)) continue;
//					_qsData[i] = pData[i];
//				}
//			}
//		}
				
//		/**
//		 * Compares two URLs, excluding the fragment component.
//		 * Returns true if this URL and the other argument are equal without taking the fragment component into consideration.
//		 * 
//		 * @param pOtherUrl the URL to compare against.
//		 * @return true if they reference the same remote object; false otherwise.
//		 */
//		public function sameFile(pOtherUrl:Url):Boolean {
//			
//		}
	}
}
